/* Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *    
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.test.jinjector.util;

import java.io.IOException;

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;

/**
 * A utility class to handle file connections. 
 * 
 * <p>This class depends to JSR75FC and cannot be included in builds for 
 * devices not supporting that library.
 * 
 * @author Michele Sama
 *
 */
public class FileConnectionUtil {

  /**
   * This file separator is for files on the target runtime platform, e.g. the
   * emulator or on a phone. 
   * 
   * <p>We initially tried using a System property e.g.
   * <code>System.getProperty("file.separator").charAt(0);</code> however that
   * returns \ on Windows for Sun's WTK simulator. 
   * 
   * <p>
   * TODO: find a way to correctly detect the filesystem separator for
   * the target runtime platform. For Nokia I think \ is used. Note: we could
   * scan the full filename for both characters and use the one we find on the
   * assumption virtually no filenames would include both \ and /.
   */
  public static final char FILE_SEPARATOR = '/';
  
  /**
   * Uninstantiable because it is a utility class.
   */
  private FileConnectionUtil() {  
  }
  
  /**
   * Closes the given file connection without propagating an IO exception.
   *
   * @param fc connection to close or null
   */
  public static void close(FileConnection fc) {
    if (fc != null) {
      try {
        fc.close();
      } catch (IOException e) {
        Log.log(FileConnectionUtil.class,
            "Caught IOException while closing fileConnection " + e.getMessage());
      }
    }
  }

 /**
  * Opens a file (existing files will be overwritten). Creates the file if it
  * does not exist.
  *
  * @param filename The name of the file to create.
  * @return a file connection to the file.
  * @throws IOException if the file cannot be created or if the filename
  *     is invalid.
  * @throws IllegalArgumentException if the filename is <code>null</code>.
  */
  public static FileConnection createAndOpenFile(String filename) 
      throws IOException {
    if (filename == null) {
      throw new IllegalArgumentException("Filename cannot be null.");
    }
    FileConnection fc = null;

    try {
      fc = (FileConnection) Connector.open(filename);
      
      if (fc.isDirectory()) {
        throw new IllegalArgumentException("Filename appears to be " +
            "a directory.");
      }
      
      if (!fc.exists()) {
        try {
        String containingDir = filename.substring(0, 
            filename.lastIndexOf(FILE_SEPARATOR));
        createDirectoriesRecursively(containingDir);
        } catch (StringIndexOutOfBoundsException sioobe) {
          throw new RuntimeException("Could not find File separator " + 
              FILE_SEPARATOR + "\nFilename:" + filename + "\n" +
              "This exception was generated by: " + sioobe.getMessage());
        }
        fc.create();
      }
      return fc;

    } catch (IOException ex) {
      close(fc);
      throw ex;
    }
  }
  
  /**
   * Creates recursively the folder structure for a file given the foldername.
   * 
   * <p>foldernames are in the format <i>"file:///SDCard/dirs/"</i> or 
   * <i>"file://localhost/SDCard/dirs/"</i> which implies that the 
   * directories starts from the fourth separator. 
   * 
   * @param foldername 
   * @throws IOException
   */
  public static void createDirectoriesRecursively(String foldername)
      throws IOException {
    
    if (!foldername.endsWith("" + FILE_SEPARATOR)) {
      foldername = foldername + FILE_SEPARATOR;
    }
    
    Log.log(FileConnectionUtil.class, 
        "createDirectoriesRecursively: " + foldername);
    
    /* Gets the index of the root separator, for instance for a foldername as
     * <i>"file:///SDCard/dirs/"</i> it will be the index of the "/" 
     * before "dirs".
     */
    final int memorySeparatorIndex = 4;
    int rootSeparatorIndex = 0;
    for (int i = 0; i < memorySeparatorIndex; i++) {
      rootSeparatorIndex = foldername.indexOf(FILE_SEPARATOR, 
          rootSeparatorIndex + 1);
    }
    
    String rootName = foldername.substring(0, rootSeparatorIndex + 1);
    String relativeName = foldername.substring(rootSeparatorIndex + 1);
    String[] dirs = StringUtil.split(relativeName, FILE_SEPARATOR);
    
    FileConnection fc = null;
    String currentFolder = rootName;
    for (int i = 0; i < dirs.length; i++) {
      if ("".equals(dirs[i])) {
        continue;
      }
      currentFolder = currentFolder + FILE_SEPARATOR + dirs[i];
      try {
        fc = (FileConnection) Connector.open(currentFolder);
        if (!fc.exists()) {
          fc.mkdir();
        }
      } finally {
        close(fc);
      }
    }
  }
}
